package sdp;

/** Abstract class that keeps track of the state of the world. This includes the state of the two robots and the ball, as well
 * as the sensor values of the player's robot. It also stores the desired wheel speeds (that are set by Control), which get
 * sent to the robot every time step.
 * The class is abstract because it doesn't provide the implementation for the world update (method update()). There are two
 * classes which inherit this one: SimWorld and NxtWorld:
 * - In the case of the SimWorld, the update simulates one time step using the wheel speeds provided and (possibly) generates
 *   an image that is then passed to Vision to estimate the new positions and orientations of the robots.
 * - The NxtWorld corresponds to the real, physical world. The desired wheel speeds are communicated to the robot, using the
 *   Communication class. The latter is also used to fetch an image from the camera which is processed by a Vision object to
 *   estimate the new positions and orientations which will be available to the Strategy in the next time step.
 */
public abstract class World {
	/** Class to wrap all the parameters needed to specify the initial state of the world. */
	public static class Settings {
		public Vector2    pitchBottomLeft = null;
		public Vector2    pitchTopRight   = null;
		public RobotState playerState     = null;
		public RobotState opponentState   = null;
		public BallState  ballState       = null;
		public int        motorCount      = -1;
		
		public boolean isValid() {
			return pitchBottomLeft != null &&
					pitchTopRight  != null &&
					ballState      != null &&
					playerState    != null &&
					opponentState  != null &&
					motorCount     >= 0    &&
					playerState.getTeam() != opponentState.getTeam();
		}
	}
	
	/** Construct using given settings. */
	public World( Settings settings ) {
		assert settings != null;
		assert settings.isValid();
		
		this.pitchBottomLeft = settings.pitchBottomLeft;
		this.pitchTopRight   = settings.pitchTopRight;
		this.playerState     = settings.playerState;
		this.opponentState   = settings.opponentState;
		this.ballState       = settings.ballState;
		this.bumper          = false;
		this.encoders        = new double[ settings.motorCount ];
		this.motorSpeeds     = new double[ settings.motorCount ];
		
		for( int i = 0 ; i < settings.motorCount ; ++ i )
			this.encoders[ i ] = this.motorSpeeds[ i ] = 0.0;
	}
	
	/** Get the team the player is on. */
	public RobotState.Team getPlayerTeam() {
		return playerState.getTeam();
	}
	
	/** Get the state of the player. */
	public RobotState getPlayerState() {
		return playerState;
	}
	
	/** Get the state of the opponent. */
	public RobotState getOpponentState() {
		return opponentState;
	}
	
	/** Get the ball's state. */
	public BallState getBallState() {
		return ballState;
	}
	
	/** Get the position of the pitch's bottom left corner. */
	public Vector2 getPitchBottomLeftCorner() {
		return pitchBottomLeft;
	}
	
	/** Get the position of the pitch's bottom right corner. */
	public Vector2 getPitchUpperRightCorner() {
		return pitchTopRight;
	}
	
	/** Check whether the player's bumper is currently pressed. */
	public boolean isBumperPressed() {
		return bumper;
	}
	
	/** Get the number of motors of the player robot. */
	public int getMotorCount() {
		return encoders.length;
	}
	
	/** Get the value of the encoders (wheel rotation counters). */
	public double[] getEncoderValues() {
		return encoders;
	}
	
	/** Get the time elapsed between the last two updates. */
	public double getTimeStep() {
		return 0.032;
	}
	
	/** Set the desired motor speed for the next update. */
	public void setMotorSpeeds( double[] newMotorSpeeds ) {
		motorSpeeds = newMotorSpeeds;
	}
	
	/** Perform one time step. */
	public void step() {
		update();
	}
	
	protected abstract void update();
	
	protected RobotState playerState;
	protected RobotState opponentState;
	protected BallState  ballState;
	protected boolean    bumper;
	protected double[]   encoders;
	protected double[]   motorSpeeds;
	
	private   Vector2    pitchBottomLeft;
	private   Vector2    pitchTopRight;
}
